package com.tedu.api.io;

import org.junit.Test;

import java.io.*;
import java.nio.charset.StandardCharsets;

/** IO流：传输数据
 *  File文件、文件夹类：无法读写数据
 *  RandomAccessFile:读写按照字节，读写类不明确，靠构造器传入第二个参数确定
 *
 * Java  提出一套 标准  IO--读写单独提供类
 * ①方向区别：
 * 输入流---（读：针对文件，文件往程序中走，文件”相对于程序“）
 * 输出流---（写：针对文件，文件从程序中写出，文件”相对于程序“）
 * 输入流和输出流创建方式？
 * 输入流   InputStream(抽象类）---顶级父类---读操作
 * 输出流   OutputStream(抽象类)---顶级父类---写操作
 * 以上两个流定义所有流的读写操作，本身无法实例化
 * ②按照功能进行区分：
 * 低级流：专门搬运数据的流 【叫做：节点流】
 * FileInputSteam     文件输入流：读操作 --- InputStream(抽象类）
 * FileOutputStream   文件输出流：写操作 --- OutputStream(抽象类)
 * 功能：建立在程序与数据源之间的”管道“所有数据传输通过低级流【节点流】
 * 高级流：不能独立存在的流【叫做：处理流】
 *       处理低级流的数据，并且可以被其他高级流所处理!
 *       理解：处理搬运数据的流【高级流种类很多】
 * -------------------流：明确功能，创建对象：直接使用低级流--高级流创建
 * 项目：  聊天室  Version 1   IO流：针对传字符串
 */
public class IOAPI {
    @Test
    public void testIO() throws IOException {
        //创建标准IO流，方向划分：输入流与输出流--功能：低级流【节点流】
        //需求：从项目中写出”fox.txt"  低级流：FileOutputStream
        //创建文件输出流  ---构造器（”相对路径+文件全称“）
        // 抛出异常FileNotFoundException   没有文件的异常！
        // 原因：路径或者文件名写错
        FileOutputStream fos = new FileOutputStream("fos.txt");
        /**
         * 输出一句话:我们是祖国的花朵
         * write(字符串：字符数组)   返回值:void
         * 作用：写出一个对应内容，字节数组
         * 抛出异常   IOException   IO流异常：写出失败
         */
        //String  字符串  方法：getBytes()  可以将字符串转换成字符数组  char[]
        fos.write("我们是祖国的花朵".getBytes());
        //标准IO流必须关闭
        fos.close();//释放流：避免内存占用
    }

    /**
     * FileOutputStream实现类----构造器使用方法
     * new FileOutputSrtream(String path,boolean append)
     * 通过第二个参数，确定数据是否追加，true代表追加，反之
     * new FileOutputStream(File file,boolean append);
     */
    @Test
    public void testIOA() throws IOException {
        //当前 API 项目 下添加fos.txt--增加功能：追加效果
        //相对路径：./fos.txt   直接写文件|不写./   都是当前目录下
        FileOutputStream fos = new FileOutputStream("fos.txt", true);
        String str = "是未来~";
        fos.write(str.getBytes());
        //关闭流
        fos.close();


    }

    /**
     * 低级流
     * 文件输入流
     * FileInputStream
     * read(字节数组)   返回值：int
     * 作用：读取到一个字节
     */
    @Test
    public void testInputStream() throws IOException {
        //创建文件输入流---读入
        FileInputStream fis = new FileInputStream("fos.txt");
        //10KB   字节数组
        byte[] bytes = new byte[1024 * 10];
        //10KB    读  数据   【不想看到整数值】
        int len = fis.read(bytes);//len  返回-1   文件读取完毕
        //String字符串类--提供构造器方法：按照字节数组量，输出字符串
        //String构造器  new String(字节数组，开始读取文件，结束读取文件);
        //将【二进制值】整数 转换位为 字符串
        String str = new String(bytes, 0, len);//循环
        //一次性将给定的字节数组【10KB】写入到文件中，文件从0开始写入，到-1结束，写入完毕
        System.out.println("打印读：" + str);
        fis.close();
    }

    //需求：【标准IO】重构RandomAccessFile类---复制视频的方法
    @Test
    public void testWork() throws IOException {
        //1.创建输入流---读----被复制文件【源文件】
        FileInputStream fis = new FileInputStream("复制文件.mp4");
        //2.创建输出流---写---复制品
        FileOutputStream fos = new FileOutputStream("标准IO.mp4");
        //3.用于保存读取到的字节量
        int len = -1;//保存读取的数据值【可以被覆盖】②-1代表文件结尾
        //6.视频空间大，解决方案：10KB，字节数组：设定保存值10KB
        byte[] bytes = new byte[1024 * 10];
        //4.一直读取【10KB】文件，保存len中，如果结果不等于-1，一直读
        //(len=fis.read(bytes))!=-1
        long start=System.currentTimeMillis();//开始毫秒数
        while ((len=fis.read(bytes))!=-1) {
            //4.1将读取到的【10KB】文件写出
            fos.write(bytes, 0, len);
        }
        long end=System.currentTimeMillis();//结束毫秒数
        System.out.println("测试传输时间："+(end-start)+"毫秒数");
            //5.关闭流
            fis.close();
            fos.close();
        }

    /**
     * 高级流【处理流】：处理低级流以及其他高级流的一”种”流
     * 高级流不能独立存在，一定处理低级流
     * BufferedInputStream----高级流：快速读    缓冲输入流
     * BufferedOutputStream---高级流：快速写    缓冲输出流
     * 以上两个高级流，内嵌一套按照实际传输数据大小，随机型改变的字节数组
     * 高级流语法：
     * BufferInputStream bis=new BufferInputStream(低级流引用);
     * 需求：视频  使用 IO流 【低级流+高级流】--实现视频快速复制
     */
    @Test
    public void testCV() throws IOException {
        //1.创建低级流：输入流:高级流、输出流：高级流
        FileInputStream fis=new FileInputStream("复制文件.mp4");
        BufferedInputStream bis=new BufferedInputStream(fis);

        FileOutputStream fos=new FileOutputStream("缓冲文件.mp4");
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        //2.定义整数变量保存每次读取的字节量
        int len=-1;
        //3.将高级流：缓冲输入流--读取的数据---保存交给len，结果不等于-1
        //(len=bis.read())!=-1
        long start=System.currentTimeMillis();
        //read()  读取【缓冲字符输入流：自动产生合理字节数组】-->读取
        while((len=bis.read())!=-1){
        //write()  读取【缓冲字符输出流：自动产生合理字节数组】-->写出
        bos.write(len);
        }
        long end=System.currentTimeMillis();
        System.out.println("缓冲流花费时间："+(end-start));
        //4.关闭流
        fis.close();
        fos.close();
    }

    /**【高级流：处理流】
     * BufferedOutputStream   缓冲输出流
     * 需求：写出一份文件：bos.txt,写出：一段文本内容
     * 所有的流 全部都不是正确的写法 IO流异常最多  采取捕获异常并做处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出 bos.txt
        FileOutputStream fos=null;
        BufferedOutputStream bos=null;
        try {
            fos=new FileOutputStream("bos.txt");
            //创建高级流--写出 一段文本内容【缓冲区：字节数组】
            bos=new BufferedOutputStream(fos);
            String str="50块钱再好看，也不如100块钱招人稀罕";
            //write(byte[] b)    作用：写出字节数组
            bos.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
        /**
         * str 数据量小   缓冲区大   强制送出【flush】   被动送出【closed】
         * 45人          车 100人                      晚上9点
         * flush()
         * 作用：可以强制将内部缓冲区数据一次性读写
         * 缺点：频繁使用flush降低读写
         */
        //bos.flush();
        if(bos!=null)
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(fos!=null)
        try {
           fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 标准IO  必须与  异常   存在   ----“传输数据”保障数据安全【处理异常】
     * 异常重点:1.认识代码中抛出异常【查API】，异常产生原因！
     *         2.异常的处理----【ctrl+alt+T】
     *         语法：try{
     *         怀疑可能有错误或者已经提示异常
     *         }catch(小异常名字 e){
     *           e.printStackTrace(); //打印异常信息---通过异常信息解决问题
     *         }catch(中异常名字 e){  catch可以捕获很多异常:小到大
     *           e.printStackTrace();
     *         }finally{
     *             一定执行代码块
     *         }
     * ------------------------------------------------
     *  异常大小问题：
     *  IO流:FileNotFoundException>IOException
     *      Exception顶级父类
     * --------------------------------------------------
     * 低级流：FileOutputStream  文件输出流---写出
     *        FileInputStream   文件输入流---读入
     * 高级流：BufferedOutputStream   缓冲输出流--写出--功能：缓冲区
     *        BufferedInputStream    缓冲输入流--读入--功能：缓冲区
     *
     *
     *         考试题:final与finally的区别？
     *         final关键字：修饰 变量、方法、类-->变量成常量
     *                                        方法不能重写
     *                                        类不能被继承
     *         finally关键字：异常处理机制中：捕获异常
     *               finally在捕获异常中，可以写，也可以不写
     *       写finally关键字所有异常都不符合，一定会执行的语法块
     *       通常使用在IO流中：关闭流为主！关闭高级流-->间接关闭低级流
     *
     *       需求:BOS高级流---重新结合异常--->标准IO流
     */
    @Test
    public void testBOSIO(){
        //1.需要将流置空--①覆盖，不用多次创建！②关闭流
        FileOutputStream fos=null; //低级流---搬运数据：写
        BufferedOutputStream bos=null;//高级流---处理低级流:缓冲输出流
        //2.捕获异常操作:快捷键   alt+ctrl+T:怀疑IO流代码---》捕获异常
        //2.1 创建输出流
        try {
            fos=new FileOutputStream("BOS标准流写法.txt");
            bos=new BufferedOutputStream(fos);
            //3.高级流：缓冲输出流---功能：缓冲区【字节数组】    写出数据
            bos.write("标准流写法".getBytes());
            //4.缓冲区:数据量比较小---采用flush()   数据一次性写出
        } catch(FileNotFoundException e){// 文件不存在异常：路径写错，文件不存在
            e.printStackTrace();//打印异常信息
            System.out.println("【小】文件不存在异常");
        } catch (IOException e) {    //IO流最大的异常：小异常不处理
            e.printStackTrace();
            System.out.println("【中】IO流异常");
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("【大】异常");
        } finally {
            //一定执行代码块：IO流关闭--高级流--关闭高级流--低级流关闭【捕获异常】
           //                       低级流---关闭低级流 【捕获异常】
           //5.先判断，高级流--是否为空【有没有高级流对象】---有高级流对象【不为null】
           //                                           没有高级流对象【不必关闭】
          if(bos!=null)
              try {
                  bos.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
        }
    }
    }

